package cn.iocoder.yudao.module.api.pay.order.vo;

import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.validation.InEnumCode;
import cn.iocoder.yudao.module.api.pay.order.eums.UnifiedOrderStatusEnum;
import cn.iocoder.yudao.module.api.pay.order.eums.UnifiedPayDisplayModeEnum;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * 支付单信息 Response DTO
 *
 * @author 芋道源码
 */
@Data
@Accessors(chain = true)
public class UnifiedOrderVO {

    @Schema(description = "支付状态：0-待付款、10-已支付、20-已退款、30-已关闭", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
    @InEnum(UnifiedOrderStatusEnum.class)
    private Integer status;

    @Schema(description = "支付单号", requiredMode = Schema.RequiredMode.REQUIRED, example = "UPY202406271105343545")
    private String payNo;

    @Schema(description = "渠道支付单号", example = "202406271105343545")
    private String channelOrderNo;

    @Schema(description = "渠道支付用户编号", example = "1024")
    private String channelUserId;

    @Schema(description = "支付成功时间", example = "2025-01-12 23:38:00")
    private LocalDateTime successTime;

    @Schema(description = "支付渠道原始的同步/异步通知结果")
    private Object rawData;

    @Schema(description = "展示模式：noe、url、iframe、vo、qr_code、qr_code_url、bar_code、app，参见 PayDisplayModeEnum 枚举类。如果不传递，则每个支付渠道使用默认的方式", example = "app")
    @InEnumCode(UnifiedPayDisplayModeEnum.class)
    private String displayMode = UnifiedPayDisplayModeEnum.NONE.getCode();

    @Schema(description = "展示内容", example = """
            {
                "appId": "wx2d697917a11930df",
                "timeStamp": "1736695760",
                "nonceStr": "mtZmLreJ8yQlPwIh",
                "package": "prepay_id=wx12232920133984a1ea9fde924e15040001",
                "signType": "MD5",
                "paySign": "C3878FA3C3E1AF5810669258A50CFCDD"
            }
            """)
    private String displayContent;

    @Schema(description = "调用渠道的错误码。注意：这里返回的是业务异常，而是不系统异常；如果是系统异常，则会抛出 PayException", example = "1000")
    private String channelErrorCode;

    @Schema(description = "调用渠道报错时的错误信息", example = "交易订单不存在")
    private String channelErrorMsg;

    private UnifiedOrderVO() {
        super();
    }

    /**
     * 创建【WAITING】状态的订单返回
     */
    public static UnifiedOrderVO waitingOf(String displayMode, String displayContent, String payNo, String channelOrderNo, Object rawData) {
        UnifiedOrderVO vo = new UnifiedOrderVO();
        vo.status = UnifiedOrderStatusEnum.WAITING.getStatus();
        vo.displayMode = displayMode;
        vo.displayContent = displayContent;
        vo.payNo = payNo;
        vo.channelOrderNo = channelOrderNo;
        vo.rawData = rawData;
        return vo;
    }

    /**
     * 创建【WAITING】状态的订单返回
     */
    public static UnifiedOrderVO waitingOf(String payNo, String channelOrderNo, Object rawData) {
        UnifiedOrderVO vo = new UnifiedOrderVO();
        vo.status = UnifiedOrderStatusEnum.WAITING.getStatus();
        vo.payNo = payNo;
        vo.channelOrderNo = channelOrderNo;
        vo.rawData = rawData;
        return vo;
    }

    /**
     * 创建【SUCCESS】状态的订单返回
     */
    public static UnifiedOrderVO successOf(String channelOrderNo, String channelUserId, LocalDateTime successTime, String payNo, Object rawData) {
        UnifiedOrderVO vo = new UnifiedOrderVO();
        vo.status = UnifiedOrderStatusEnum.SUCCESS.getStatus();
        vo.channelOrderNo = channelOrderNo;
        vo.channelUserId = channelUserId;
        vo.successTime = successTime;
        // 相对通用的字段
        vo.payNo = payNo;
        vo.rawData = rawData;
        return vo;
    }

    /**
     * 创建【CLOSED】状态的订单返回，适合调用支付渠道失败时
     */
    public static UnifiedOrderVO closedOf(String channelErrorCode, String channelErrorMsg, String payNo, Object rawData) {
        UnifiedOrderVO vo = new UnifiedOrderVO();
        vo.status = UnifiedOrderStatusEnum.CLOSED.getStatus();
        vo.channelErrorCode = channelErrorCode;
        vo.channelErrorMsg = channelErrorMsg;
        // 相对通用的字段
        vo.payNo = payNo;
        vo.rawData = rawData;
        return vo;
    }

    /**
     * 创建指定状态的订单返回，适合支付渠道回调时
     */
    public static UnifiedOrderVO ofNotify(Integer status, String channelOrderNo, String channelUserId, LocalDateTime successTime, String payNo, String channelErrorCode, String channelErrorMsg, Object rawData) {
        UnifiedOrderVO vo = new UnifiedOrderVO();
        vo.status = status;
        vo.channelOrderNo = channelOrderNo;
        vo.channelUserId = channelUserId;
        vo.successTime = successTime;
        // 相对通用的字段
        vo.payNo = payNo;
        vo.channelErrorCode = channelErrorCode;
        vo.channelErrorMsg = channelErrorMsg;
        vo.rawData = rawData;
        return vo;
    }

    /**
     * 避坑：由于 package 为 Java 保留关键字，因此改为 packageValue，返回给前端使用时记得要更改为 package
     *
     * @see com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult
     * @see com.github.binarywang.wxpay.bean.ecommerce.TransactionsResult.JsapiResult
     * @see com.github.binarywang.wxpay.bean.result.CombineTransactionsResult.JsapiResult
     * @see com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result.JsapiResult
     */
    @SuppressWarnings("JavadocReference")
    @Data
    @NoArgsConstructor
    public static class WxJsapiResult implements Serializable {
        @JsonProperty("appId")
        private String appId;
        @JsonProperty("timeStamp")
        private String timeStamp;
        @JsonProperty("nonceStr")
        private String nonceStr;
        @JsonProperty("package")
        private String packageValue;
        @JsonProperty("signType")
        private String signType;
        @JsonProperty("paySign")
        private String paySign;
    }

    /**
     * 避坑：由于 package 为 Java 保留关键字，因此改为 packageValue，返回给前端使用时记得要更改为 package
     *
     * @see com.github.binarywang.wxpay.bean.order.WxPayAppOrderResult
     */
    @SuppressWarnings("JavadocReference")
    @Data
    @NoArgsConstructor
    public static class WxPayAppOrderResult implements Serializable {
        @JsonProperty("appId")
        private String appId;
        @JsonProperty("partnerId")
        private String partnerId;
        @JsonProperty("prepayId")
        private String prepayId;
        @JsonProperty("package")
        private String packageValue;
        @JsonProperty("nonceStr")
        private String nonceStr;
        @JsonProperty("timeStamp")
        private String timeStamp;
        @JsonProperty("sign")
        private String sign;
    }

    /**
     * 避坑：由于 package 为 Java 保留关键字，因此改为 packageValue，返回给前端使用时记得要更改为 package
     *
     * @see com.github.binarywang.wxpay.bean.ecommerce.TransactionsResult.AppResult
     * @see com.github.binarywang.wxpay.bean.result.CombineTransactionsResult.AppResult
     * @see com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result.AppResult
     */
    @SuppressWarnings("JavadocReference")
    @Data
    @NoArgsConstructor
    public static class AppResult implements Serializable {
        @JsonProperty("appid")
        private String appid;
        @JsonProperty("partnerId")
        private String partnerId;
        @JsonProperty("prepayId")
        private String prepayId;
        @JsonProperty("package")
        private String packageValue;
        @JsonProperty("noncestr")
        private String noncestr;
        @JsonProperty("timestamp")
        private String timestamp;
        @JsonProperty("sign")
        private String sign;
    }

}
